// Effective C++ 条款42：了解typename的双重含义

// 核心概念
// 条款42讨论了typename关键字在C++中的两种用途：
// 1.作为class关键字的替代品，用于定义模板参数
// 2.指明嵌套依赖类型名称是一个类型而非静态成员

// typename作为class的替代品
// 在定义模板参数时，typename和class是等价的：
template <class T>
class Widget
{
    ...
}; // 使用class
template <typename T>
class Widget
{
    ...
}; // 使用typename
// 这两种写法完全等价，选择使用哪一个纯粹是风格问题。一些程序员倾向于使用typename，
// 因为它更明确地表示参数可以是任何类型，而不仅仅是类类型。

// typename的特殊用途：嵌套依赖类型名称
// typename的第二个用途更为重要：它用于告诉编译器一个嵌套在模板参数中的名称是一个类型。

// 依赖名称和非依赖名称
// 在模板中，我们区分：
//  -非依赖名称：不依赖于模板参数的名称
//  -依赖名称：依赖于模板参数的名称
template <typename T>
void func()
{
    std::vector<int> vec; // 非依赖名称
    T::size_type value;   // 依赖名称 - 依赖于T
}

// 嵌套依赖类型名称的问题(C是个模板，const_iterator是C里的成员)
// 当我们使用嵌套在模板参数中的类型时，编译器无法确定它是一个类型还是一个静态成员：
template <typename C>
void print2nd(const C &container)
{
    C::const_iterator iter; // C::const_iterator是什么？类型还是静态成员？
    ...
}
// C++规则规定：如果解析器在模板中遇到一个嵌套依赖名称，它会假设这个名称不是一个类型，除非你告诉它是。
// 使用typename指明类型
// 为了告诉编译器一个嵌套依赖名称是一个类型，我们必须使用typename关键字：

template <typename C>
void print2nd(const C &container)
{
    typename C::const_iterator iter; // 现在明确告诉编译器这是一个类型
    ...
}

// 何时需要typename
// 需要使用typename的情况：
//      1.嵌套依赖类型名称（依赖于模板参数的嵌套类型）
//      2.不包括基类列表或成员初始化列表中的基类标识
template <typename T>
class MyClass : public T::Base
{ // 基类列表中不需要typename
public:
    explicit MyClass(int x)
        : T::Base(x)
    { // 成员初始化列表中不需要typename

        typename T::InnerType *ptr; // 需要typename

        typedef typename T::OtherType OtherTypeAlias; // 需要typename

        typename T::OtherType::NestedType var; // 需要typename
    }
};

// 实际例子
// 迭代器类型
template <typename Container>
void processContainer(const Container &c)
{
    // 错误：没有typename
    Container::iterator iter = c.begin();

    // 正确：使用typename
    typename Container::iterator iter = c.begin();
}

// 返回类型
template <typename T>
typename T::value_type getValue(const T &container, int index)
{
    return container[index];
}

// 模板元编程
template <typename T>
struct RemoveConst
{
    typedef T type;
};

template <typename T>
struct RemoveConst<const T>
{
    typedef T type;
};

template <typename T>
void process(T &value)
{
    typename RemoveConst<T>::type nonConstValue = value;
    // 使用nonConstValue...
}

// 使用typedef简化代码
// 使用typedef可以简化复杂的嵌套依赖类型名称：
template <typename IterT>
void workWithIterator(IterT it)
{
    // 复杂的嵌套依赖类型名称
    typename std::iterator_traits<IterT>::value_type temp(*it);

    // 使用typedef简化
    typedef typename std::iterator_traits<IterT>::value_type value_type;
    value_type temp2(*it);
}

// 现代C++中的简化
// C++11引入了auto和decltype，可以在某些情况下避免使用复杂的typename声明：
template <typename Container>
void processContainer(const Container &c)
{
    // C++98/03方式
    typename Container::iterator iter = c.begin();

    // C++11方式
    auto iter = c.begin();
}

// C++14引入了decltype(auto)，进一步简化了返回类型推导：
template <typename Container>
auto getValue(const Container &c, int index) -> decltype(c[index])
{
    return c[index];
}

// C++14
template <typename Container>
decltype(auto) getValue(const Container &c, int index)
{
    return c[index];
}

// 总结
// 条款42的要点：
// 1.在模板参数声明中，typename和class是等价的
// 2.使用typename标识嵌套依赖类型名称
// 3.不要在基类列表或成员初始化列表的基类标识中使用typename
// 4.使用typedef和现代C++特性（如auto）可以简化复杂的类型声明
// 5.理解typename的双重含义对于编写正确的模板代码至关重要，特别是在处理复杂的嵌套类型时。
